/*
 * Flashlib - QB9 common Flash library
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


package com.qb9.flashlib.input.mouse
{
	import com.qb9.flashlib.events.QEventDispatcher;
	import com.qb9.flashlib.geom.Direction;
	import com.qb9.flashlib.geom.Vector2D;
	
	import flash.display.DisplayObject;
	import flash.events.MouseEvent;
	import flash.utils.clearTimeout;
	import flash.utils.setTimeout;

	/**
	 * Monitors mouse movement and dispatches events for linear motions
	 */

	// TODO: Diagonals are supported but the mouseMove doesn't give time to achieve them
	// Maybe use an interval or enter frame 
	
	public final class MouseMovement extends QEventDispatcher
	{
		private var context:DisplayObject;
		
		private var expireTime:uint;
		private var tileSize:uint;
		
		private var current:Vector2D;
		private var lastDir:int;
		private var timeoutId:int;
		
		/**
		 * Creates a new MouseMovement Object
		 * 
		 * @param context The object where mouse movement should be tracked
		 * @param tileSize Controls the sensivity, moving within a tile doesn't trigger eventsç
		 * @param expireTime Time to wait before release buffered movements. Pass 0 for no expiring.
		 */  
		public function MouseMovement(context:DisplayObject, tileSize:uint=30, expireTime:uint=800)
		{
			this.context = context;
			this.tileSize = tileSize;
			this.expireTime = expireTime;
		}
		
		/** Starts monitoring movement */
		public function start():void
		{
			context.addEventListener(MouseEvent.MOUSE_MOVE, checkMovement);
			current = null;
			lastDir = 0;
		}
		
		private function getCurrentTile():Vector2D
		{
			return new Vector2D(
				Math.floor(context.mouseX / tileSize),
				Math.floor(context.mouseY / tileSize)
			);
		}
		
		private function checkMovement(e:MouseEvent):void
		{
			var prev:Vector2D = current;
			current = getCurrentTile();
			
			if (!prev || prev.equals(current))
				return;
				
			var dir:int = Direction.calculate(prev, current); 
			if (dir === lastDir)
				return;
			
			notify();
			
			lastDir = dir;
			scheduleNotify();
		}
		
		private function notify():void
		{
			if (!lastDir)
				return;
			
			// For every movement, we dispatch a generic catch-all event
			notifyEvent(MouseMovementEvent.MOVEMENT);
			

			var vertical:String = lastDir & Direction.NORTH ? MouseMovementEvent.UP   : 
			                      lastDir & Direction.SOUTH ? MouseMovementEvent.DOWN :
			                      '';
							  
			var horizontal:String = lastDir & Direction.EAST? MouseMovementEvent.RIGHT: 
			                        lastDir & Direction.WEST? MouseMovementEvent.LEFT :
			                        '';
							  
			notifyEvent(vertical+horizontal);
			lastDir = 0;
		}
		
		private function notifyEvent(type:String):void
		{
			if (type)
				dispatchEvent(new MouseMovementEvent(type, lastDir));
		}
		
		private function clear():void
		{
			clearTimeout(timeoutId);
		}
		
		private function scheduleNotify():void
		{
			if (!expireTime)
				return;
			
			clear();
			timeoutId = setTimeout(expire, expireTime);
		}
		
		/** Releases a buffered movement */
		public function expire():void
		{
			clear();
			notify();
		}
		
		/** Stops monitoring movement */
		public function stop():void
		{
			context.removeEventListener(MouseEvent.MOUSE_MOVE, checkMovement);
			clear();
		}
		
		/** @inheritDoc */
		override public function dispose():void
		{
			stop();
			context = null;
			super.dispose();
		}
		
	}
}